package keeper

import (
	"context"
	"encoding/json"
	"gitee.com/andrewgithub/EtcdKeeperFyne/utils"
	clientv3 "go.etcd.io/etcd/client/v3"
)

type Cfg struct {
	Endpoints []string `json:"endpoints"`
	UserName  string   `json:"user_name"`
	PassWord  string   `json:"pass_word"`
}

// Client 连接etcd服务端的client
type Client struct {
	cli *clientv3.Client
	cfg Cfg
}

func NewClient(cfg *Cfg) (*Client, error) {
	// from 12739
	etcdCfg := clientv3.Config{
		Endpoints: cfg.Endpoints,
		Username:  cfg.UserName,
		Password:  cfg.PassWord,
	}
	var cli Client
	cli.cfg = *cfg
	var err error
	cli.cli, err = clientv3.New(etcdCfg)

	return &cli, err
}

func (cli *Client) GetUserName() string {
	return cli.cfg.UserName
}

func (cli *Client) GetPassWord() string {
	return cli.cfg.PassWord
}

func (cli *Client) GetEtcdConn() *clientv3.Client {
	return cli.cli
}

func (cli *Client) GetEndpoints() []string {
	if cli == nil {
		return []string{}
	}
	return cli.cfg.Endpoints
}

func (cli *Client) SetEndpoints(host string) {
	cli.cfg.Endpoints = append(cli.cfg.Endpoints, host)
}

func (cli *Client) Equal(host string) bool {
	if cli == nil {
		return false
	}

	// 目前只支持一个endpoints
	if cli.cfg.Endpoints[0] != host {
		return false
	}

	return true
}

func (cli *Client) Status(ctx context.Context) error {
	if cli.cli == nil {
		return utils.NewError("cli is nil.")
	}

	resp, err := cli.cli.Status(ctx, cli.GetEndpoints()[0])
	if err != nil {
		respStr, _ := json.Marshal(resp)
		return utils.NewError(string(respStr))
	}
	return nil
}

func (cli *Client) RangeAllKvs() (*clientv3.GetResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}

	return cli.cli.Get(context.TODO(), "", clientv3.WithPrefix())
}

func (cli *Client) GetValue(key string) (*clientv3.GetResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}

	return cli.cli.Get(context.TODO(), key)
}

func (cli *Client) DeleteKeys(key string) error {
	if cli.cli == nil {
		return utils.NewError("client is nil, please check!")
	}

	_, err := cli.cli.Delete(context.TODO(), key)
	if err != nil {
		return err
	}
	return nil
}

func (cli *Client) AddKv(key, value string, leaseId int64) (*clientv3.PutResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}
	rsp, err := cli.cli.Put(context.TODO(), key, value, clientv3.WithPrevKV(), clientv3.WithLease(clientv3.LeaseID(leaseId)))
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

func (cli *Client) UpdateValue(key, value string) (*clientv3.PutResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}

	rsp, err := cli.cli.Put(context.TODO(), key, value, clientv3.WithIgnoreLease(), clientv3.WithPrevKV())
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

// GrantLease 创建租约ID
func (cli *Client) GrantLease(ttl int64) (*clientv3.LeaseGrantResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}
	rsp, err := cli.cli.Grant(context.TODO(), ttl)
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

// TimeToAliveTime 获取当前租约剩余时间
func (cli *Client) TimeToAliveTime(leaseId clientv3.LeaseID) (*clientv3.LeaseTimeToLiveResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}

	rsp, err := cli.cli.TimeToLive(context.TODO(), leaseId, clientv3.WithAttachedKeys())
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

func (cli *Client) KeepAliveOnce(leaseId clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}

	rsp, err := cli.cli.KeepAliveOnce(context.TODO(), leaseId)
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

// RevokeLease 清楚对应的lease
func (cli *Client) RevokeLease(leaseId clientv3.LeaseID) (*clientv3.LeaseRevokeResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}
	rsp, err := cli.cli.Revoke(context.TODO(), leaseId)
	if err != nil {
		return nil, err
	}
	return rsp, nil
}

// Leases 获取多有的LeaseId
func (cli *Client) Leases() (*clientv3.LeaseLeasesResponse, error) {
	if cli.cli == nil {
		return nil, utils.NewError("client is nil, please check!")
	}
	rsp, err := cli.cli.Leases(context.TODO())
	if err != nil {
		return nil, err
	}
	return rsp, nil
}
